package com.example.springdatajpaderivedquerymethod.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.springdatajpaderivedquerymethod.inter.EmployeeSalary;
import com.example.springdatajpaderivedquerymethod.model.Employee;
import com.example.springdatajpaderivedquerymethod.repositories.EmployeePagingAndSortingRepository;
import com.example.springdatajpaderivedquerymethod.repositories.EmployeeRepository;
import com.example.springdatajpaderivedquerymethod.service.EmployeeDAOService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.repository.query.Param;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping(value = "/employee")
public class EmployeeController {

    private EmployeeRepository employeeRepository;
    private EmployeePagingAndSortingRepository pagingAndSortingRepository;
    @Autowired
    private EmployeeDAOService employeeDAOService;

    @Autowired
    public EmployeeController(EmployeeRepository employeeRepository, EmployeePagingAndSortingRepository pagingAndSortingRepository) {
        this.employeeRepository = employeeRepository;
        this.pagingAndSortingRepository = pagingAndSortingRepository;
    }

    @PostMapping(value = "/saveAll",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> saveAll(@RequestBody List<Employee> employees){
        employeeRepository.saveAll(employees);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/findByDept",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByDept(@RequestParam("dept") final String dept){
        List<Employee> employees = employeeRepository.findByDept(dept);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findBySalaryGreaterThan",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findBySalaryGreaterThan(@RequestParam("salary") final Integer salary){
        List<Employee> employees = employeeRepository.findBySalaryGreaterThan(salary);
        return new ResponseEntity<>(employees, HttpStatus.OK);
    }

    @GetMapping(value = "/findByDeptAndSalaryLessThan",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByDeptAndSalaryLessThan(@RequestParam("dept") final String dept,
                                                                      @RequestParam("salary") final Integer salary){
        List<Employee> employees = employeeRepository.findByDeptAndSalaryLessThan(dept,salary);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findTopByOrderBySalary",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Employee> findTopByOrderBySalary(@RequestParam("direction") final String direction){
        Employee employee = null;
        if (direction.equals("desc")) {
            employee = employeeRepository.findTopByOrderBySalaryDesc();
        }else{
            employee = employeeRepository.findTopByOrderBySalaryAsc();
        }
        return new ResponseEntity<>(employee,HttpStatus.OK);
    }

    @GetMapping(value = "/findTop3ByOrderBySalary",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findTop3ByOrderBySalary(@RequestParam("direction") final String direction){
        List<Employee> employees = new ArrayList<>();
        if (direction.equals("asc")) {
            employees = employeeRepository.findTop3ByOrderBySalaryAsc();
        }else{
            employees = employeeRepository.findTop3ByOrderBySalaryDesc();
        }
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findFirst2ByDeptOrderBySalaryDesc",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findFirst2ByDeptOrderBySalaryDesc(@RequestParam("dept") final String dept){
        List<Employee> employees = employeeRepository.findFirst2ByDeptOrderBySalaryDesc(dept);
        return new ResponseEntity<>(employees, HttpStatus.OK);
    }

    @GetMapping(value = "/findByNameLike",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByNameLike(@RequestParam("name") final String name){
        String like = "%" + name + "%";
        List<Employee> employees = employeeRepository.findByNameLike(like);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/countBy",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> countBy(@RequestParam("endString") final String endString,
                                        @RequestParam("like") final String like){
        long endCount = employeeRepository.countByNameEndingWith(endString);
        System.err.println("endCount=" + endCount);
        String likeString = "%" + like + "%";
        long likeCount = employeeRepository.countByNameLike(likeString);
        System.err.println("likeCount=" + likeCount);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/findMaxSalariesByDept",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Object[]>> findMaxSalaryByDept(){
        List<Object[]> objectList = employeeRepository.findMaxSalariesByDept(Arrays.asList("IT","Sales"));
        return new ResponseEntity<>(objectList, HttpStatus.OK);
    }

    @GetMapping(value = "/findMinSalariesByDept",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Object[]>> findMinSalariesByDept(){
        List<Object[]> objects = employeeRepository.findMinSalariesByDept(Arrays.asList("IT","Sales"));
        return new ResponseEntity<>(objects, HttpStatus.OK);
    }

    @GetMapping(value = "/findByNameContaining",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByNameLike(@RequestParam("type") final Integer type,@RequestParam("name") final String name){
        List<Employee> employees = new ArrayList<>();
        if (type.equals(1)) {
            employees = employeeRepository.findByName("%" + name + "%");
        }else if(type.equals(2)){
            employees = employeeRepository.findByName2(name);
        }else if(type.equals(3)){
            employees = employeeRepository.findByNameContaining(name);
        }
        return new ResponseEntity<>(employees, HttpStatus.OK);
    }

    @GetMapping(value = "/findMaxMinAvgSalariesByDept",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> findMaxMinAvgSalariesByDept(){
        List<List<Integer>> data = employeeRepository.findMaxMinAvgSalariesByDept(Arrays.asList("IT","Sales"));
        System.err.println(JSONObject.toJSONString(data,true));
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/countByDeptNamed",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Long> countByDeptNamed(@RequestParam("dept") final String dept){
        Long count = employeeRepository.countByDeptNamed(dept);
        return new ResponseEntity<>(count,HttpStatus.OK);
    }

    @GetMapping(value = "/findByDepartment",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByDepartment(@RequestParam("department") final String department){
        List<Employee> employees = employeeRepository.findByDepartment(department);
        return new ResponseEntity<>(employees, HttpStatus.OK);
    }

    @GetMapping(value = "/findByDeptSorted",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByDeptSorted(@RequestParam("dept") final String dept,
                                                           @RequestParam("direction") final String direction){
        Sort sort = null;
        if (direction.equals("asc")) {
            sort = Sort.by("salary","name").ascending();
        }else if(direction.equals("desc")){
            sort = Sort.by("salary","name").descending();
        }
        List<Employee> employees = employeeRepository.findByDept(dept,sort);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findByDeptWithPageable",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Page<Employee>> findByDeptWithPageable(@RequestParam(value = "page",required = false,defaultValue = "0") final Integer page,
                                                                 @RequestParam(value = "size",required = false,defaultValue = "2") final Integer size){
        Sort sort = Sort.by("salary").descending();
        Pageable pageable = PageRequest.of(page,size,sort);
        Page<Employee> employees = employeeRepository.findByDept("Sales",pageable);
        return new ResponseEntity<>(employees, HttpStatus.OK);

    }

    @GetMapping(value = "/getEmployeesByPageable",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Page<Employee>> getEmployeesByPageable(@PageableDefault(page = 0,size = 2,sort = "id",direction = Sort.Direction.ASC) Pageable pageable){
        Page<Employee> employees = pagingAndSortingRepository.findAll(pageable);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/updateSalaryByPercent",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> updateSalaryByPercent(@RequestParam("dept") final String dept,
                                                      @RequestParam("percent") final Integer percent){
        employeeRepository.updateDeptSalaries(dept, percent);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/deleteEmployee",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> deleteEmployee(){
        employeeRepository.deleteByDeptAndName("Sales","Sara");
        employeeRepository.deleteBySalaryGreaterThanEqual(6000);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/interfaceBaseProjections",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> interfaceBaseProjections(){
        List<EmployeeSalary> employeeSalaries = employeeRepository.findBy();
        for (EmployeeSalary employeeSalary : employeeSalaries) {
            System.err.println("name= " + employeeSalary.getName() + ", salary= " + employeeSalary.getSalary());
        }
        System.err.println(String.join("", Collections.nCopies(50,"-")));
        List<EmployeeSalary> employeeBetweens = employeeRepository.findBySalaryBetween(3000,5000);
        for (EmployeeSalary employeeBetween : employeeBetweens) {
            System.err.println("name= " + employeeBetween.getName() + ", salary= " + employeeBetween.getSalary());
        }
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/findEmployeeByNamePredicate",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> findEmployeeByNamePredicate(@RequestParam("name") final String name){
        List<Employee> employees = employeeDAOService.findByCriteria(name);
        System.err.println(JSONObject.toJSONString(employees,true));
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @GetMapping(value = "/findEmployeeByDeptNameAndSalary",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findEmployeeByDeptNameAndSalary(@RequestParam(value = "deptName",required = false) final String deptName,
                                                                          @RequestParam(value = "salary",required = false) final Integer salary){
        List<Employee> employees = employeeDAOService.findByCriteria(deptName,salary);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findEmployeeByDeptNameAndSalaryBetween",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findEmployeeByDeptNameAndSalaryBetween(@RequestParam(value = "deptName",required = false) final String deptName,
                                                                                 @RequestParam(value = "salaryMin",required = false) final Integer salaryMin,
                                                                                 @RequestParam(value = "salaryMax",required = false) final Integer salaryMax){
        List<Employee> employees = employeeDAOService.findByDeptNameAndSalaryBetween(deptName, salaryMin, salaryMax);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findByDeptNameAndPageable",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByDeptNameAndPageable(@RequestParam("deptName") final String deptName,
                                                                    @RequestParam(value = "page",required = false,defaultValue = "0") final Integer page,
                                                                    @RequestParam(value = "size",required = false,defaultValue = "2") final Integer size){
        Pageable pageable = PageRequest.of(page,size);
        List<Employee> employees = employeeDAOService.findByPagingCriteria(deptName,pageable);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findEmployeeByDeptNameAndOrderBy",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findEmployeeByDeptNameAndOrderBy(@RequestParam(value = "direction",required = false,defaultValue = "asc") final String direction){
        List<Employee> employees = employeeDAOService.findByCriteriaOrderBy("Sales","salary",direction);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findByLikeAndBetween",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Employee>> findByLikeAndBetween(@RequestParam(value = "text",defaultValue = "",required = false) final String text,
                                                               @RequestParam(value = "salaryStart",required = false) final Integer salaryStart,
                                                               @RequestParam(value = "salaryEnd",required = false) final Integer salaryEnd){
        List<Employee> employees = employeeDAOService.findByLikeAndBetween(text,salaryStart,salaryEnd);
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }

    @GetMapping(value = "/findByNamesIn",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String,List<Employee>>> findByNamesIn(@RequestParam("names") final List<String> names){
        List<Employee> employees = employeeDAOService.findByInCriteria(names);
        Map<String,List<Employee>> map = new HashMap<>();
        map.put("namesIn",employees);
        List<Employee> employeesNotIn = employeeDAOService.findByNotInCriteria(names);
        map.put("namesNotIn",employeesNotIn);
        return new ResponseEntity<>(map,HttpStatus.OK);
    }

    @GetMapping(value = "/findByDeptNative",produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Page<Employee>> findByDeptNative(@RequestParam(value = "page",required = false,defaultValue = "0") final Integer page,
                                                           @RequestParam(value = "size",required = false,defaultValue = "2") final Integer size){
        Page<Employee> employees = employeeRepository.findByDeptNative("Sales",PageRequest.of(page,size));
        return new ResponseEntity<>(employees,HttpStatus.OK);
    }
}
